#include	"alp_global_vars.h"
#include	"config_parser.h"

void look4highest(int start, int end) {
	int i;
	for (i = start; i < end; i++) {
		if (data[(i % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].deliver)
			alp_interval_performance->highest_deliverable_inst_id = i;
		else
			break;
	}
}

void execute_phase_2_b(paxos_msg *msg) {

	int i = msg->instance_number;
	int r = msg->round_number;

	assert(data[(i % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].packet.pack_id == data[(i % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].instance_id + 1);

	if (data[(i % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].packet.pack_id == data[(i % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].instance_id + 1 && data[(i % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].received) {
		//		if (paxos_instances[(i % INTERNAL_BUFFER_SIZE)].rnd <= r) {
		//		if (paxos_instances[(i % INTERNAL_BUFFER_SIZE)].vrnd != r) {
		//				paxos_instances[(i % INTERNAL_BUFFER_SIZE)].rnd = r;
		//				paxos_instances[(i % INTERNAL_BUFFER_SIZE)].vrnd = r;
		msg->acceptor_count++;
		msg->type = PHASE_2_B;

		//			} else {
		//				if (paxos_instances[(i % INTERNAL_BUFFER_SIZE)].vrnd == r) { // This is a hack: we assume the value proposed is the same...

		//					msg->acceptor_count++;
		//					msg->type = PHASE_2_B;
		//				}
		//			}
		//}
	}
}

void a_handel_msg_paxos(paxos_msg *m) {

	alp_interval_performance->interval_num_paxos_packets_received++;

	if (m->type == PHASE_2_B) {
		execute_phase_2_b(m);

		if (get_config_info()->self_acceptor_id == get_config_info()->total_num_acceptors - 1) {// lost acceptor, is the one who finds out the hD
			if (m->acceptor_count == N) {
				if (data[(m->instance_number % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].received) {
					acceptor_0_deliver(m->instance_number, m->VALUE);// what to happen here?
					if (m->instance_number > gapEnd)
						gapEnd = m->instance_number;
					look4highest(alp_interval_performance->highest_deliverable_inst_id + 1, gapEnd + 1);
					learner_deliver();
				}
			}
		}
	}

}

void a_handel_msg_inst_pack(inst_paxos * inst_paxos_messg) {

	int status = -1;
	int self_id = get_config_info()->self_acceptor_id;

	inst_paxos_messg->inst_packet.packet.p_msgs[0].num_nodes_visited++;
	handle_packet(&(inst_paxos_messg->inst_packet)); //handles and updates the packet
	a_handel_msg_paxos(&(inst_paxos_messg->paxos_2b));

	if (get_config_info()->self_acceptor_id == (get_config_info()->total_num_acceptors - 1)) {
		if (inst_paxos_messg->inst_packet.packet.p_msgs[0].num_nodes_visited == get_config_info()->ring_size) {
			short_dec_notification short_decision;
			short_decision.highestDeliverable = alp_interval_performance->highest_deliverable_inst_id;

			if (inst_paxos_messg->paxos_2b.acceptor_count == N) {
				short_decision.instance_id = inst_paxos_messg->inst_packet.instance_id;
				short_decision.location_index_in_proposer = inst_paxos_messg->inst_packet.packet.p_msgs[0].location_index_in_proposer;
				short_decision.pack_id = inst_paxos_messg->inst_packet.packet.pack_id;
				short_decision.proposer_id = inst_paxos_messg->inst_packet.packet.p_msgs[0].proposer_id;
				short_decision.proposer_msg_id = inst_paxos_messg->inst_packet.packet.p_msgs[0].msg_id;
			} else {
				short_decision.instance_id = -1;
				//				printf("PROPOSER: %d 	packet: %d	VOTES: %d msg_id: %d	hd: %d\n", inst_paxos_messg->inst_packet.packet.p_msgs[0].proposer_id, inst_paxos_messg->inst_packet.packet.pack_id, inst_paxos_messg->paxos_2b.acceptor_count, inst_paxos_messg->inst_packet.packet.p_msgs[0].msg_id, alp_interval_performance->highest_deliverable_inst_id);
			}

			short_decision.type = 's';
			status = sendto((get_config_info()->server_infos[self_id].successor_sid), &(short_decision), sizeof(short_dec_notification), 0, (struct sockaddr*) &(get_config_info()->server_infos[self_id].ring_successor_addr), sizeof(struct sockaddr_in));
			assert(status == sizeof(short_dec_notification));
			alp_interval_performance->interval_bytes_sent += status;

		} else {
			inst_paxos_messg->inst_packet.highestDeliverable = alp_interval_performance->highest_deliverable_inst_id;
			inst_paxos_messg->inst_packet.type = 'i';
			status = send((get_config_info()->server_infos[self_id].successor_sid), &(inst_paxos_messg->inst_packet), sizeof(inst), MSG_WAITALL);
			assert(status == sizeof(inst));

			alp_interval_performance->interval_bytes_sent += status;
		}
	} else {
		if (inst_paxos_messg->inst_packet.packet.p_msgs[0].num_nodes_visited == get_config_info()->ring_size) {
			paxos_short_not pd_msg;
			pd_msg.type = 'm';
			memcpy(&(pd_msg.paxos_2b), &(inst_paxos_messg->paxos_2b), sizeof(paxos_data));

			pd_msg.short_decision.highestDeliverable = alp_interval_performance->highest_deliverable_inst_id;
			pd_msg.short_decision.instance_id = inst_paxos_messg->inst_packet.instance_id;
			pd_msg.short_decision.location_index_in_proposer = inst_paxos_messg->inst_packet.packet.p_msgs[0].location_index_in_proposer;
			pd_msg.short_decision.pack_id = inst_paxos_messg->inst_packet.packet.pack_id;
			pd_msg.short_decision.proposer_id = inst_paxos_messg->inst_packet.packet.p_msgs[0].proposer_id;
			pd_msg.short_decision.proposer_msg_id = inst_paxos_messg->inst_packet.packet.p_msgs[0].msg_id;
			pd_msg.short_decision.type = 's';

			status = send((get_config_info()->server_infos[self_id].successor_sid), &(pd_msg), sizeof(paxos_short_not), MSG_WAITALL);
			assert(status == sizeof(paxos_short_not));

		} else {
			inst_paxos_messg->type = 'n';
			status = send((get_config_info()->server_infos[self_id].successor_sid), inst_paxos_messg, sizeof(inst_paxos), MSG_WAITALL);
			assert(status == sizeof(inst_paxos));
		}
		alp_interval_performance->interval_bytes_sent += status;
		learner_deliver();
	}

}

void a_handle_paxos_only(paxos_short_not *pd_msg) {

	int status;
	int self_id = get_config_info()->self_acceptor_id;

	data[(pd_msg->short_decision.instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].received = true;
	data[(pd_msg->short_decision.instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].deliver = false;
	data[(pd_msg->short_decision.instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].highestDeliverable = -1;
	data[(pd_msg->short_decision.instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].packet.p_msgs[0].location_index_in_proposer = pd_msg->short_decision.location_index_in_proposer;
	data[(pd_msg->short_decision.instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].packet.pack_id = pd_msg->short_decision.pack_id;
	data[(pd_msg->short_decision.instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].instance_id = pd_msg->short_decision.instance_id;
	assert(data[(pd_msg->short_decision.instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].packet.pack_id == data[(pd_msg->short_decision.instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].instance_id + 1);
	data[(pd_msg->short_decision.instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].packet.p_msgs[0].msg_id = pd_msg->short_decision.proposer_msg_id;
	data[(pd_msg->short_decision.instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].packet.p_msgs[0].proposer_id = pd_msg->short_decision.proposer_id;
	data[(pd_msg->short_decision.instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].packet.p_msgs[0].mili_seconds = p_msgs[pd_msg->short_decision.proposer_id][pd_msg->short_decision.location_index_in_proposer % INTERNAL_BUFFER_SIZE].mili_seconds;

	a_handel_msg_paxos(&(pd_msg->paxos_2b));

	if (get_config_info()->self_acceptor_id == (get_config_info()->total_num_acceptors - 1)) {
		pd_msg->short_decision.highestDeliverable = alp_interval_performance->highest_deliverable_inst_id;

		if (pd_msg->paxos_2b.acceptor_count != N)
			pd_msg->short_decision.instance_id = -1;
		pd_msg->short_decision.type = 's';

		// IN CASE NOT EVERYONE IS SENDING, THIS LINES SHOULD BE CUNCOMMNETED. or if you have different learner-proposers
		 status = sendto((get_config_info()->server_infos[self_id].successor_sid), &(pd_msg->short_decision), sizeof(short_dec_notification), 0, (struct sockaddr*) &(get_config_info()->server_infos[self_id].ring_successor_addr), sizeof(struct sockaddr_in));
		 assert(status == sizeof(short_dec_notification));
		 alp_interval_performance->interval_bytes_sent += status;

	} else {
		alp_interval_performance->highest_deliverable_inst_id = pd_msg->short_decision.highestDeliverable;
		status = sendto((get_config_info()->server_infos[self_id].successor_sid), (pd_msg), sizeof(paxos_short_not), 0, (struct sockaddr*) &(get_config_info()->server_infos[self_id].ring_successor_addr), sizeof(struct sockaddr_in));
		assert(status == sizeof(paxos_short_not));
		alp_interval_performance->interval_bytes_sent += status;
	}
	learner_deliver();
}

void handle_packet(inst *new_data) {

	rtag = true;

	if (alp_interval_performance->highest_deliverable_inst_id < new_data->highestDeliverable) {
		alp_interval_performance->highest_deliverable_inst_id = new_data->highestDeliverable;
		learner_deliver();
	}
	memcpy(&data[(new_data->instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))], new_data, sizeof(inst));
	data[(new_data->instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].received = true;
	data[(new_data->instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].deliver = false;
	data[(new_data->instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].highestDeliverable = -1;
	assert(data[(new_data->instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].packet.pack_id == data[(new_data->instance_id % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].instance_id + 1);
	alp_interval_performance->interval_num_packets_received++;

}
